package cryptoutil

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"sync"
)

type KeyStore interface {
	GetCACert() []byte
	GetSignCert() []byte
	GetPrivateKey() Key
	GetVersion() string
}

type keystore struct {
	CACert []byte
	SignCert []byte
	Key Key
	Version string
	IssuerPublicKey []byte
	IssuerRevocationPublicKey []byte
}

func (ks keystore) GetCACert() []byte {
	return ks.CACert
}

func (ks keystore) GetSignCert() []byte {
	return ks.SignCert
}

func (ks keystore) GetPrivateKey() Key {
	return ks.Key
}

func (ks keystore) GetVersion() string {
	return ks.Version
}

func LoadKeyStoreFromPath(path string, pwd []byte) (KeyStore, error) {
	ks := keystore{}
	var err error
	buffers, err := getPemMaterialFromDir(filepath.Join(path, "signcerts"))
	if err != nil && len(buffers) != 1 {
		return nil, err
	}
	if len(buffers) != 1 {
		return nil, fmt.Errorf("Wrong number of files")
	}
	ks.SignCert = buffers[0]
	keycerts, err := getPemMaterialFromDir(filepath.Join(path, "keystore"))
	if err != nil {
		return nil, err
	}
	key, err := GetPrivateKeyFromPEM(keycerts[0], pwd)
	if err != nil {
		return nil, err
	}
	ks.Key = key
	return ks, nil
}

func LoadKeyStoreFromBytes(certBytes, keyBytes, keyPwd []byte)(KeyStore, error) {
	// TODO
	//cert, _ := getCertFromPem(certBytes)
	key, err := GetPrivateKeyFromPEM(keyBytes, keyPwd)
	if err != nil {
		return nil, err
	}
	return &keystore{SignCert:certBytes, Key:key}, nil
}

func LoadKeyStoreFromFile(certfile, keyfile string, keyPwd []byte) (KeyStore, error) {
	certBytes, err := ioutil.ReadFile(certfile)
	if err != nil {
		return nil, err
	}
	keybytes, err := ioutil.ReadFile(keyfile)
	if err != nil {
		return nil, err
	}
	return LoadKeyStoreFromBytes(certBytes, keybytes, keyPwd)
}

type PrivateKey interface {
	FileName() string
	GetBytes(pwd []byte) ([]byte, error)
}

type privateKey struct {
	key Key
}

func NewPrivateKey(key Key) PrivateKey {
	return privateKey{key:key}
}

func (prk privateKey) FileName() string {
	if prk.key.Private() {
		return fmt.Sprintf("%x_sk", prk.key.SKI())
	}
	return ""
}

func (prk privateKey) GetBytes(pwd []byte) ([]byte, error) {
	return GetPEMFromPrivateKey(prk.key, pwd)
	//return privateKeyToPEM(prk.key, pwd)
}

type memoryKeyStore struct {
	keys  map[string]Key
	mux  sync.RWMutex
}

func (mks memoryKeyStore) ReadOnly() bool {
	return false
}
